import serial
import serial.tools.list_ports
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import tkinter as tk
from tkinter import ttk, messagebox
import threading
import time
import sys

class SerialWaveformPlotter:
    def __init__(self, root):
        self.root = root
        self.root.title("双路串口波形绘制器（带信号检测）")
        self.root.geometry("1600x800")
        self.root.minsize(1400, 700)  # 增大最小窗口尺寸适应新功能
        
        # 中文显示配置
        plt.rcParams["font.family"] = ["SimHei", "WenQuanYi Micro Hei", "Heiti TC"]
        plt.rcParams["axes.unicode_minus"] = False
        
        # 串口核心变量
        self.ser = None
        self.is_connected = False
        self.data_buffer = bytearray()
        self.running = True
        
        # 双波形数据缓存（1024点/路）+ 参数缓存
        self.wave1_data = [0] * 1024
        self.wave2_data = [0] * 1024
        self.param_cache = {
            "freq1": 0,   # 波形一频率（Hz）
            "freq2": 0,   # 波形二频率（Hz）
            "amp1": 0,    # 波形一幅度
            "amp2": 0     # 波形二幅度
        }
        
        # 串口参数配置字典
        self.parity_options = {
            "无": serial.PARITY_NONE,
            "奇校验": serial.PARITY_ODD,
            "偶校验": serial.PARITY_EVEN,
            "标记": serial.PARITY_MARK,
            "空格": serial.PARITY_SPACE
        }
        self.stopbits_options = {
            "1": serial.STOPBITS_ONE,
            "1.5": serial.STOPBITS_ONE_POINT_FIVE,
            "2": serial.STOPBITS_TWO
        }
        self.bytesize_options = {
            "5": serial.FIVEBITS,
            "6": serial.SIXBITS,
            "7": serial.SEVENBITS,
            "8": serial.EIGHTBITS
        }
        
        # 创建主布局（4列：波形区2列，参数区1列，检测区1列）
        self.main_frame = ttk.Frame(root)
        self.main_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
        self.main_frame.grid_columnconfigure(0, weight=2)  # 波形区
        self.main_frame.grid_columnconfigure(1, weight=1)  # 参数区
        self.main_frame.grid_columnconfigure(2, weight=1)  # 信号检测区
        self.main_frame.grid_rowconfigure(0, weight=1)     # 波形一+参数一+检测区上
        self.main_frame.grid_rowconfigure(1, weight=1)     # 波形二+参数二+检测区下
        
        # 1. 创建顶部串口设置栏（跨所有列）
        self.create_serial_settings()
        
        # 2. 创建波形1显示区（左）+ 波形1参数区（中）
        self.create_wave_param_panel(0, "波形一")
        
        # 3. 创建波形2显示区（左）+ 波形2参数区（中）
        self.create_wave_param_panel(1, "波形二")
        
        # 4. 创建信号检测窗口（右侧，跨两行）
        self.create_detection_panel()
        
        # 初始化数据解析线程
        self.parse_thread = threading.Thread(target=self.parse_data, daemon=True)
        self.parse_thread.start()

    def create_serial_settings(self):
        """创建顶部串口设置框架"""
        settings_frame = ttk.LabelFrame(self.main_frame, text="串口设置")
        settings_frame.grid(row=0, column=0, columnspan=3, padx=5, pady=5, sticky="ew")
        settings_frame.grid_columnconfigure(11, weight=1)
        
        # 端口选择
        ttk.Label(settings_frame, text="端口:").grid(row=0, column=0, padx=5, pady=5, sticky="w")
        self.port_var = tk.StringVar()
        self.port_combo = ttk.Combobox(settings_frame, textvariable=self.port_var, width=12)
        self.port_combo.grid(row=0, column=1, padx=5, pady=5, sticky="w")
        
        # 波特率选择
        ttk.Label(settings_frame, text="波特率:").grid(row=0, column=2, padx=5, pady=5, sticky="w")
        self.baudrate_var = tk.StringVar(value="115200")
        self.baudrate_combo = ttk.Combobox(settings_frame, textvariable=self.baudrate_var, width=10)
        self.baudrate_combo['values'] = ['9600', '19200', '38400', '57600', '115200', '230400', '460800', '921600']
        self.baudrate_combo.grid(row=0, column=3, padx=5, pady=5, sticky="w")
        
        # 数据位/校验位/停止位
        ttk.Label(settings_frame, text="数据位:").grid(row=0, column=4, padx=5, pady=5, sticky="w")
        self.bytesize_var = tk.StringVar(value="8")
        self.bytesize_combo = ttk.Combobox(settings_frame, textvariable=self.bytesize_var, width=5)
        self.bytesize_combo['values'] = list(self.bytesize_options.keys())
        self.bytesize_combo.grid(row=0, column=5, padx=5, pady=5, sticky="w")
        
        ttk.Label(settings_frame, text="校验位:").grid(row=0, column=6, padx=5, pady=5, sticky="w")
        self.parity_var = tk.StringVar(value="无")
        self.parity_combo = ttk.Combobox(settings_frame, textvariable=self.parity_var, width=6)
        self.parity_combo['values'] = list(self.parity_options.keys())
        self.parity_combo.grid(row=0, column=7, padx=5, pady=5, sticky="w")
        
        ttk.Label(settings_frame, text="停止位:").grid(row=0, column=8, padx=5, pady=5, sticky="w")
        self.stopbits_var = tk.StringVar(value="1")
        self.stopbits_combo = ttk.Combobox(settings_frame, textvariable=self.stopbits_var, width=5)
        self.stopbits_combo['values'] = list(self.stopbits_options.keys())
        self.stopbits_combo.grid(row=0, column=9, padx=5, pady=5, sticky="w")
        
        # 功能按钮
        self.refresh_btn = ttk.Button(settings_frame, text="刷新端口", command=self.refresh_ports)
        self.refresh_btn.grid(row=0, column=10, padx=5, pady=5, sticky="e")
        
        self.connect_btn = ttk.Button(settings_frame, text="连接", command=self.toggle_connection)
        self.connect_btn.grid(row=0, column=11, padx=5, pady=5, sticky="e")
        
        # 连接状态
        self.status_var = tk.StringVar(value="未连接")
        self.status_label = ttk.Label(settings_frame, textvariable=self.status_var, foreground="red")
        self.status_label.grid(row=0, column=12, padx=10, pady=5, sticky="e")
        
        # 初始刷新端口
        self.refresh_ports()

    def create_wave_param_panel(self, row_idx, wave_name):
        """创建单个波形显示区+参数区面板"""
        # 波形显示框架（左侧）
        wave_frame = ttk.LabelFrame(self.main_frame, text=f"{wave_name} 波形（无符号十进制 0~255）")
        wave_frame.grid(row=row_idx+1, column=0, padx=5, pady=5, sticky="nsew")
        
        # 参数显示框架（中间）
        param_frame = ttk.LabelFrame(self.main_frame, text=f"{wave_name} 参数")
        param_frame.grid(row=row_idx+1, column=1, padx=5, pady=5, sticky="nsew")
        param_frame.grid_columnconfigure(0, weight=1)
        
        # 初始化波形图表
        if wave_name == "波形一":
            self.fig1, self.ax1 = plt.subplots(figsize=(8, 4), dpi=100)
            self.line1, = self.ax1.plot(self.wave1_data, color='#ff7f0e', label="波形一")  # 橙色
            self.ax1.set_ylim(-10, 270)
            self.ax1.set_title(f"{wave_name}（1024点/帧）")
            self.ax1.set_xlabel("数据点序号")
            self.ax1.set_ylabel("数值（0~255）")
            self.ax1.grid(alpha=0.3)
            self.ax1.legend()
            
            # 嵌入GUI
            from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
            self.canvas1 = FigureCanvasTkAgg(self.fig1, master=wave_frame)
            self.canvas1.get_tk_widget().pack(fill=tk.BOTH, expand=True)
            
            # 波形1参数显示
            ttk.Label(param_frame, text="频率（Hz）:", font=("Arial", 12)).grid(row=0, column=0, padx=20, pady=30, sticky="w")
            self.freq1_var = tk.StringVar(value="0")
            self.freq1_label = ttk.Label(param_frame, textvariable=self.freq1_var, font=("Arial", 14, "bold"), foreground="#ff7f0e")
            self.freq1_label.grid(row=0, column=1, padx=20, pady=30, sticky="w")
            
            ttk.Label(param_frame, text="幅度:", font=("Arial", 12)).grid(row=1, column=0, padx=20, pady=30, sticky="w")
            self.amp1_var = tk.StringVar(value="0")
            self.amp1_label = ttk.Label(param_frame, textvariable=self.amp1_var, font=("Arial", 14, "bold"), foreground="#ff7f0e")
            self.amp1_label.grid(row=1, column=1, padx=20, pady=30, sticky="w")
            
            # 波形1动画
            self.ani1 = animation.FuncAnimation(self.fig1, self.update_wave1, interval=100, blit=True)
        
        else:  # 波形二
            self.fig2, self.ax2 = plt.subplots(figsize=(8, 4), dpi=100)
            self.line2, = self.ax2.plot(self.wave2_data, color='#1f77b4', label="波形二")  # 蓝色
            self.ax2.set_ylim(-10, 270)
            self.ax2.set_title(f"{wave_name}（1024点/帧）")
            self.ax2.set_xlabel("数据点序号")
            self.ax2.set_ylabel("数值（0~255）")
            self.ax2.grid(alpha=0.3)
            self.ax2.legend()
            
            # 嵌入GUI
            from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
            self.canvas2 = FigureCanvasTkAgg(self.fig2, master=wave_frame)
            self.canvas2.get_tk_widget().pack(fill=tk.BOTH, expand=True)
            
            # 波形2参数显示
            ttk.Label(param_frame, text="频率（Hz）:", font=("Arial", 12)).grid(row=0, column=0, padx=20, pady=30, sticky="w")
            self.freq2_var = tk.StringVar(value="0")
            self.freq2_label = ttk.Label(param_frame, textvariable=self.freq2_var, font=("Arial", 14, "bold"), foreground="#1f77b4")
            self.freq2_label.grid(row=0, column=1, padx=20, pady=30, sticky="w")
            
            ttk.Label(param_frame, text="幅度:", font=("Arial", 12)).grid(row=1, column=0, padx=20, pady=30, sticky="w")
            self.amp2_var = tk.StringVar(value="0")
            self.amp2_label = ttk.Label(param_frame, textvariable=self.amp2_var, font=("Arial", 14, "bold"), foreground="#1f77b4")
            self.amp2_label.grid(row=1, column=1, padx=20, pady=30, sticky="w")
            
            # 波形2动画
            self.ani2 = animation.FuncAnimation(self.fig2, self.update_wave2, interval=100, blit=True)

    def create_detection_panel(self):
        """创建信号检测窗口（右侧）"""
        detection_frame = ttk.LabelFrame(self.main_frame, text="信号检测设置")
        detection_frame.grid(row=1, column=2, rowspan=2, padx=5, pady=5, sticky="nsew")
        detection_frame.grid_rowconfigure(5, weight=1)  # 让按钮区域靠下
        
        # 通道一幅度范围
        ttk.Label(detection_frame, text="通道一幅度范围 (V):", font=("Arial", 12)).grid(row=0, column=0, columnspan=2, padx=10, pady=20, sticky="w")
        
        ttk.Label(detection_frame, text="A (最小值):", font=("Arial", 10)).grid(row=1, column=0, padx=10, pady=10, sticky="e")
        self.amp1_min_var = tk.StringVar(value="0.000")
        self.amp1_min_entry = ttk.Entry(detection_frame, textvariable=self.amp1_min_var, width=10, font=("Arial", 10))
        self.amp1_min_entry.grid(row=1, column=1, padx=10, pady=10, sticky="w")
        
        ttk.Label(detection_frame, text="B (最大值):", font=("Arial", 10)).grid(row=2, column=0, padx=10, pady=10, sticky="e")
        self.amp1_max_var = tk.StringVar(value="5.000")
        self.amp1_max_entry = ttk.Entry(detection_frame, textvariable=self.amp1_max_var, width=10, font=("Arial", 10))
        self.amp1_max_entry.grid(row=2, column=1, padx=10, pady=10, sticky="w")
        
        # 通道二幅度范围
        ttk.Label(detection_frame, text="通道二幅度范围 (V):", font=("Arial", 12)).grid(row=3, column=0, columnspan=2, padx=10, pady=20, sticky="w")
        
        ttk.Label(detection_frame, text="C (最小值):", font=("Arial", 10)).grid(row=4, column=0, padx=10, pady=10, sticky="e")
        self.amp2_min_var = tk.StringVar(value="0.000")
        self.amp2_min_entry = ttk.Entry(detection_frame, textvariable=self.amp2_min_var, width=10, font=("Arial", 10))
        self.amp2_min_entry.grid(row=4, column=1, padx=10, pady=10, sticky="w")
        
        ttk.Label(detection_frame, text="D (最大值):", font=("Arial", 10)).grid(row=5, column=0, padx=10, pady=10, sticky="e")
        self.amp2_max_var = tk.StringVar(value="5.000")
        self.amp2_max_entry = ttk.Entry(detection_frame, textvariable=self.amp2_max_var, width=10, font=("Arial", 10))
        self.amp2_max_entry.grid(row=5, column=1, padx=10, pady=10, sticky="w")
        
        # 发送按钮
        self.send_btn = ttk.Button(
            detection_frame, 
            text="发送幅度范围设置", 
            command=self.send_detection_params,
            style="Accent.TButton"
        )
        self.send_btn.grid(row=6, column=0, columnspan=2, padx=20, pady=40)
        
        # 发送状态提示
        self.send_status_var = tk.StringVar(value="未发送")
        self.send_status_label = ttk.Label(detection_frame, textvariable=self.send_status_var, foreground="gray")
        self.send_status_label.grid(row=7, column=0, columnspan=2, padx=10, pady=10)

    def decimal_to_bcd(self, decimal_str):
        """将X.XXX格式的字符串转换为2字节BCD码（忽略小数点）"""
        # 移除小数点并补零至4位数字（如"1.234"→"1234"，"0.5"→"0500"）
        try:
            # 处理输入格式
            if '.' not in decimal_str:
                decimal_str += '.000'
            integer_part, decimal_part = decimal_str.split('.', 1)
            decimal_part = decimal_part.ljust(3, '0')[:3]  # 确保小数部分3位
            combined = (integer_part + decimal_part).ljust(4, '0')[:4]  # 总长度4位
            
            # 转换为BCD码
            bcd_bytes = []
            for i in range(0, 4, 2):
                # 每两位数字转换为1字节BCD
                high_digit = int(combined[i])
                low_digit = int(combined[i+1])
                bcd_byte = (high_digit << 4) | low_digit
                bcd_bytes.append(bcd_byte)
            
            return bytes(bcd_bytes)
        except Exception as e:
            print(f"BCD转换错误: {str(e)}")
            return None

    def send_detection_params(self):
        """发送幅度范围参数（转换为BCD码）"""
        if not self.is_connected or not self.ser or not self.ser.is_open:
            messagebox.showerror("错误", "请先连接串口")
            return
        
        try:
            # 获取输入值
            a_val = self.amp1_min_var.get().strip()
            b_val = self.amp1_max_var.get().strip()
            c_val = self.amp2_min_var.get().strip()
            d_val = self.amp2_max_var.get().strip()
            
            # 验证输入格式（X.XXX）
            def is_valid_format(s):
                parts = s.split('.')
                if len(parts) != 2:
                    return False
                if not (parts[0].isdigit() and parts[1].isdigit() and len(parts[1]) == 3):
                    return False
                return True
            
            for name, val in [("A", a_val), ("B", b_val), ("C", c_val), ("D", d_val)]:
                if not is_valid_format(val):
                    messagebox.showerror("格式错误", f"{name}格式应为X.XXX（如1.234）")
                    return
            
            # 转换为BCD码
            a_bcd = self.decimal_to_bcd(a_val)
            b_bcd = self.decimal_to_bcd(b_val)
            c_bcd = self.decimal_to_bcd(c_val)
            d_bcd = self.decimal_to_bcd(d_val)
            
            if not all([a_bcd, b_bcd, c_bcd, d_bcd]):
                messagebox.showerror("转换错误", "无法将输入转换为BCD码")
                return
            
            # 组合8字节数据并发送
            send_data = a_bcd + b_bcd + c_bcd + d_bcd
            self.ser.write(send_data)
            
            # 更新状态提示
            self.send_status_var.set(f"已发送: {send_data.hex().upper()}")
            self.send_status_label.config(foreground="green")
            
        except Exception as e:
            self.send_status_var.set(f"发送失败: {str(e)}")
            self.send_status_label.config(foreground="red")
            messagebox.showerror("发送错误", f"发送参数失败: {str(e)}")

    def bcd_to_decimal(self, bcd_bytes):
        """BCD码转十进制（处理1~4字节BCD数据）"""
        decimal_val = 0
        for byte in bcd_bytes:
            high_nibble = (byte >> 4) & 0x0F
            low_nibble = byte & 0x0F
            if high_nibble > 9 or low_nibble > 9:
                return 0  # 无效BCD返回0
            decimal_val = decimal_val * 100 + high_nibble * 10 + low_nibble
        return decimal_val

    def refresh_ports(self):
        """刷新串口端口列表"""
        try:
            ports = serial.tools.list_ports.comports()
            port_list = [f"{port.device} - {port.description}" for port in ports]
            self.port_combo['values'] = port_list
            if port_list:
                self.port_combo.current(0)
        except Exception as e:
            messagebox.showerror("错误", f"刷新端口失败: {str(e)}")

    def toggle_connection(self):
        """切换串口连接状态"""
        if self.is_connected:
            self.disconnect()
        else:
            self.connect()

    def connect(self):
        """建立串口连接"""
        if self.is_connected:
            return
        try:
            selected_port = self.port_var.get()
            if not selected_port:
                messagebox.showerror("错误", "请先选择串口端口")
                return
            port = selected_port.split(" - ")[0]
            
            # 解析串口参数
            try:
                baudrate = int(self.baudrate_var.get())
                bytesize = self.bytesize_options[self.bytesize_var.get()]
                parity = self.parity_options[self.parity_var.get()]
                stopbits = self.stopbits_options[self.stopbits_var.get()]
            except Exception as e:
                messagebox.showerror("错误", f"串口参数无效: {str(e)}")
                return
            
            # 关闭已有连接
            if self.ser and self.ser.is_open:
                self.ser.close()
            
            # 建立新连接
            self.ser = serial.Serial(
                port=port,
                baudrate=baudrate,
                bytesize=bytesize,
                parity=parity,
                stopbits=stopbits,
                timeout=0.1,
                xonxoff=False,
                rtscts=False,
                dsrdtr=False
            )
            
            if self.ser.is_open:
                self.is_connected = True
                self.connect_btn.config(text="断开")
                self.status_var.set(f"已连接: {port} @ {baudrate} Hz")
                self.status_label.config(foreground="green")
                messagebox.showinfo("成功", f"已连接到串口\n端口: {port}\n波特率: {baudrate}")
            else:
                messagebox.showerror("错误", "连接失败：串口未正常打开")
        except serial.SerialException as e:
            error_msg = f"串口错误: {str(e)}\n"
            if "Permission denied" in str(e) or "访问被拒绝" in str(e):
                error_msg += "可能原因：权限不足或串口被其他程序占用"
            messagebox.showerror("连接失败", error_msg)
            self.status_label.config(foreground="red")
        except Exception as e:
            messagebox.showerror("错误", f"连接异常: {str(e)}")
            self.status_label.config(foreground="red")

    def disconnect(self):
        """断开串口连接"""
        if self.is_connected:
            try:
                if self.ser and self.ser.is_open:
                    self.ser.close()
                self.is_connected = False
                self.connect_btn.config(text="连接")
                self.status_var.set("未连接")
                self.status_label.config(foreground="red")
                self.data_buffer.clear()
            except Exception as e:
                messagebox.showerror("错误", f"断开连接失败: {str(e)}")

    def parse_data(self):
        """解析串口数据（按帧结构）"""
        # 帧结构定义（总长度=4+4+4+2+2+1024+1024+4=2068字节）
        PACKET_HEADER = bytes([0x08, 0x08, 0x04, 0x08])
        PACKET_TAIL = bytes([0x04, 0x03, 0x09, 0x09])
        FREQ1_LEN = 4    # 波形一频率（4字节BCD）
        FREQ2_LEN = 4    # 波形二频率（4字节BCD）
        AMP1_LEN = 2     # 波形一幅度（2字节BCD）
        AMP2_LEN = 2     # 波形二幅度（2字节BCD）
        WAVE1_LEN = 1024 # 波形一数据
        WAVE2_LEN = 1024 # 波形二数据
        PACKET_TOTAL_LEN = 4 + FREQ1_LEN + FREQ2_LEN + AMP1_LEN + AMP2_LEN + WAVE1_LEN + WAVE2_LEN + 4

        while self.running:
            if self.is_connected and self.ser and self.ser.is_open:
                try:
                    if self.ser.in_waiting > 0:
                        read_data = self.ser.read(self.ser.in_waiting)
                        self.data_buffer.extend(read_data)
                        
                        while len(self.data_buffer) >= PACKET_TOTAL_LEN:
                            header_idx = self.data_buffer.find(PACKET_HEADER)
                            if header_idx == -1:
                                if len(self.data_buffer) > PACKET_TOTAL_LEN:
                                    self.data_buffer = self.data_buffer[-PACKET_TOTAL_LEN+1:]
                                break
                            
                            if len(self.data_buffer) < header_idx + PACKET_TOTAL_LEN:
                                break
                            
                            full_packet = self.data_buffer[header_idx:header_idx+PACKET_TOTAL_LEN]
                            
                            if full_packet[-4:] != PACKET_TAIL:
                                self.data_buffer = self.data_buffer[header_idx+1:]
                                continue
                            
                            # 解析帧内数据
                            freq1_bcd = full_packet[4 : 4+FREQ1_LEN]
                            self.param_cache["freq1"] = self.bcd_to_decimal(freq1_bcd)
                            
                            freq2_bcd = full_packet[4+FREQ1_LEN : 4+FREQ1_LEN+FREQ2_LEN]
                            self.param_cache["freq2"] = self.bcd_to_decimal(freq2_bcd)
                            
                            amp1_bcd = full_packet[4+FREQ1_LEN+FREQ2_LEN : 4+FREQ1_LEN+FREQ2_LEN+AMP1_LEN]
                            self.param_cache["amp1"] = self.bcd_to_decimal(amp1_bcd)
                            
                            amp2_bcd = full_packet[4+FREQ1_LEN+FREQ2_LEN+AMP1_LEN : 4+FREQ1_LEN+FREQ2_LEN+AMP1_LEN+AMP2_LEN]
                            self.param_cache["amp2"] = self.bcd_to_decimal(amp2_bcd)
                            
                            wave1_bytes = full_packet[4+FREQ1_LEN+FREQ2_LEN+AMP1_LEN+AMP2_LEN : 4+FREQ1_LEN+FREQ2_LEN+AMP1_LEN+AMP2_LEN+WAVE1_LEN]
                            self.wave1_data = [byte for byte in wave1_bytes]
                            
                            wave2_bytes = full_packet[4+FREQ1_LEN+FREQ2_LEN+AMP1_LEN+AMP2_LEN+WAVE1_LEN : 4+FREQ1_LEN+FREQ2_LEN+AMP1_LEN+AMP2_LEN+WAVE1_LEN+WAVE2_LEN]
                            self.wave2_data = [byte for byte in wave2_bytes]
                            
                            # 更新缓存
                            self.data_buffer = self.data_buffer[header_idx+PACKET_TOTAL_LEN:]
                            
                except serial.SerialException as e:
                    print(f"串口通信错误: {str(e)}")
                    self.disconnect()
                    time.sleep(1)
                except Exception as e:
                    print(f"数据解析错误: {str(e)}")
                    self.data_buffer = self.data_buffer[header_idx+1:] if 'header_idx' in locals() else self.data_buffer[1:]
                    time.sleep(0.1)
            else:
                time.sleep(0.1)

    def update_wave1(self, frame):
        """更新波形一显示+参数"""
        self.line1.set_ydata(self.wave1_data)
        min_val = min(self.wave1_data)
        max_val = max(self.wave1_data)
        padding = (max_val - min_val) * 0.1 if max_val != min_val else 10
        self.ax1.set_ylim(min_val - padding, max_val + padding)
        self.freq1_var.set(str(self.param_cache["freq1"]))
        self.amp1_var.set(str(self.param_cache["amp1"]))
        return self.line1,

    def update_wave2(self, frame):
        """更新波形二显示+参数"""
        self.line2.set_ydata(self.wave2_data)
        min_val = min(self.wave2_data)
        max_val = max(self.wave2_data)
        padding = (max_val - min_val) * 0.1 if max_val != min_val else 10
        self.ax2.set_ylim(min_val - padding, max_val + padding)
        self.freq2_var.set(str(self.param_cache["freq2"]))
        self.amp2_var.set(str(self.param_cache["amp2"]))
        return self.line2,

    def on_close(self):
        """窗口关闭时清理资源"""
        self.running = False
        self.disconnect()
        plt.close(self.fig1)
        plt.close(self.fig2)
        self.root.destroy()

if __name__ == "__main__":
    import matplotlib
    matplotlib.use('TkAgg')
    
    root = tk.Tk()
    # 添加自定义按钮样式
    style = ttk.Style()
    style.configure("Accent.TButton", font=("Arial", 10, "bold"))
    
    app = SerialWaveformPlotter(root)
    root.protocol("WM_DELETE_WINDOW", app.on_close)
    
    try:
        root.mainloop()
    except Exception as e:
        messagebox.showerror("程序错误", f"运行异常: {str(e)}")
        print(f"程序崩溃: {str(e)}")
        app.on_close()